﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Linq;

namespace Chambills.Present.Core
{
    public interface IViewRegistry
    {
        UIElement FindViewFor(object ownerViewModel);
    }

    /// <summary>
    /// Each View that wishes to particiapte in activities initiated via the ViewModel (i.e. Dialogs, StateManager) must be registered with
    /// <see cref="IViewRegistry"/> 
    /// This implemnetation is done by attached Properties
    /// ViewRegistry.Register="true" from XAML
    /// </summary>
    public class ViewRegistry : IViewRegistry
    {
        protected static readonly HashSet<FrameworkElement> Views;


        static ViewRegistry()
        {
            Views = new HashSet<FrameworkElement>();
        }



        /// <summary>
        /// Registers a View.
        /// </summary>
        /// <param name="view">The registered View.</param>
        private static void Register(FrameworkElement view)
        {
            if (Views.Contains(view)) throw new ArgumentException("View has already been registered.");
            view.Loaded += OnViewLoaded;

        }

        private static void OnViewUnloaded(object sender, RoutedEventArgs e)
        {
            Unregister((FrameworkElement)sender);
        }

        private static void OnViewLoaded(object sender, RoutedEventArgs e)
        {
            var view = (FrameworkElement)sender;
            if (!Views.Contains(view))
            {
                Views.Add(view);
                view.Unloaded += OnViewUnloaded;
            }
        }


        /// <summary>
        /// Unregisters a View.
        /// </summary>
        /// <param name="view">The unregistered View.</param>
        public static void Unregister(FrameworkElement view)
        {
            if (!Views.Contains(view)) throw new ArgumentException("View has never been registered.");

            Views.Remove(view);
            view.Unloaded -= OnViewUnloaded;
        }




        #region Attached properties

        /// <summary>
        /// Attached property describing whether a FrameworkElement is acting as a View in MVVM.
        /// </summary>
        public static readonly DependencyProperty RegisterProperty = DependencyProperty.RegisterAttached(
            "Register",
            typeof(bool),
            typeof(ViewRegistry),
            new UIPropertyMetadata(RegisterPropertyChanged));


        /// <summary>
        /// Gets value describing whether FrameworkElement is acting as View in MVVM.
        /// </summary>
        public static bool GetRegister(FrameworkElement target)
        {
            return (bool)target.GetValue(RegisterProperty);
        }


        /// <summary>
        /// Sets value describing whether FrameworkElement is acting as View in MVVM.
        /// </summary>
        public static void SetRegister(FrameworkElement target, bool value)
        {
            target.SetValue(RegisterProperty, value);
        }


        /// <summary>
        /// Is responsible for handling RegisterProperty changes, i.e. whether
        /// FrameworkElement is acting as View in MVVM or not.
        /// </summary>
        private static void RegisterPropertyChanged(DependencyObject target,
                                                    DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement view = target as FrameworkElement;
            if (view == null)
                return;

            bool newValue = (bool)e.NewValue;
            bool oldValue = (bool)e.OldValue;

            if (newValue == oldValue && newValue)
                return;

            if (newValue)
            {
                Register(view);
            }
            else
            {
                Unregister(view);
            }
        }

        #endregion

        public UIElement FindViewFor(object ownerViewModel)
        {
            return Views.FirstOrDefault(v => v.DataContext == ownerViewModel);
        }
    }
}